package bsk;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import komunikaty.Card;
import komunikaty.Kandydaci;
import komunikaty.Statistics;
import komunikaty.Upowazniony;


    
public class Connect {
    private static final int SSL_ECHO_PORT = 443;
    private static final String host = "localhost";
    //truststore server
    private String TS_PATH;// = "servers.jks"; // trusted servers
    private String TS_PASS;// = "098765";
    
	
    private SSLSocket socket;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    
    private boolean clientA;  
    private boolean doGlosowania;
    private boolean Glosowanie;
    private boolean publikacja;
    
    private String messageFromServer;
    public Kandydaci[] listaKand;
    
    //połaczenie (authorization = true), pobranie danych (doGlosowania = true), głosowanie (glosowanie = true), publikacja (publikacja = true)
    public Connect(String TS_path, String TS_pass, boolean authorization, boolean doGlosowania, boolean glosowanie, boolean publikacja){
        this.TS_PATH = TS_path;
        this.TS_PASS = TS_pass;
        this.clientA = authorization;
        this.doGlosowania = doGlosowania;
        this.Glosowanie = glosowanie;
        this.publikacja = publikacja;
    }
    
    private Upowazniony Glosujacy;
    private int kartaGlos;
    
    //połaczenie (authorization = true), pobranie danych (doGlosowania = true), głosowanie (glosowanie = true), publikacja (publikacja = true)
    public Connect(String TS_path, String TS_pass, boolean authorization, boolean doGlosowania, boolean glosowanie, boolean publikacja, Upowazniony pesel, int glos){
        this.TS_PATH = TS_path;
        this.TS_PASS = TS_pass;
        this.clientA = authorization;
        this.doGlosowania = doGlosowania;
        this.Glosowanie = glosowanie;
        this.publikacja = publikacja;
        this.Glosujacy = pesel;
        this.kartaGlos = glos;
    }
    
    public void run(){
        System.setProperty("javax.net.ssl.trustStore", TS_PATH);
	System.setProperty("javax.net.ssl.trustStorePassword", TS_PASS);
             
        try{
            SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            socket = (SSLSocket) factory.createSocket(host, SSL_ECHO_PORT);

            //Enabling all available cipher suites
            String[] suites = socket.getSupportedCipherSuites();
            socket.setEnabledCipherSuites(suites);

            //Registering a handshake listener
//             socket.addHandshakeCompletedListener(new MyHandshakeListener());//powiadomienie,że połączenie SSL zostało przeprowadzone na tym połączeniu

            //Starting handshaking
            socket.startHandshake();

            //dane połączenia, certyfikaty, itd.
//            SSLSession session = socket.getSession();
//            java.security.cert.Certificate[] cchain =  session.getPeerCertificates();
//            System.out.println("The Certificates used by peer");
//            for (int i = 0; i < cchain.length; i++) {
//                System.out.println(((X509Certificate) cchain[i]).getSubjectDN());
//            }
//            System.out.println("Peer host is " + session.getPeerHost());
//            System.out.println("Cipher is " + session.getCipherSuite());
//            System.out.println("Protocol is " + session.getProtocol());
//            System.out.println("ID is " + new BigInteger(session.getId()));
//            System.out.println("Session created in " + session.getCreationTime());
//            System.out.println("Session accessed in " + session.getLastAccessedTime());
            

            //Object in i out
            out =  new ObjectOutputStream(socket.getOutputStream());
            out.flush();
            in = new ObjectInputStream(socket.getInputStream());
            Object message = null;
            
            
            
            //przykładowy głosujący                 UUUUUUUUUUUUUUUUUUUUUUUUSUNĄĆ
//            Upowazniony U_out = new Upowazniony("jan", "kowalnski", new BigInteger("56218658203"));
            
            
            
            
            
            try{
                message = (String)in.readObject();
                System.out.println("<wyslal� server>" + message);
                boolean[] CoRobic = new boolean[4];
                CoRobic[0] = clientA;  
                CoRobic[1] = doGlosowania;
                CoRobic[2] = Glosowanie;
                CoRobic[3] = publikacja;
                
                //wysyła co ma robić serwer
                sendMessage(CoRobic);
                
                message = (String)in.readObject();
                System.out.println("<wyslal� server>" + message);
                
                if (clientA){
                    
                    //tutaj jest Tworzenie certyfikatu
                    
                    //wysyła swoje dane (PESEL)
                    sendMessage(Glosujacy);

                    //odczytuje czy może głosować (czy wcześniej nie głosował)
                    message = in.readObject();
                    System.out.println("<wysłał server>" + (String)message);
                    
                    
                    
                    if("Możesz głosować!".equals((String)message)){
                        sendMessage("dane do certyfikatu");
                        message = in.readObject();
                        System.out.println((String)message);
                    }else {
                    	messageFromServer =message.toString();
                    }
                    
                    //jesli tak to wysli to co trzeba
                    
                }else if (doGlosowania){
                    
                    //tutaj jest sprawdzanie klienta, certyfikat
                    
                    sendMessage("Prosze o liste kandydatów");
                    
                    //odczytuje liste kandydatow
                    message = in.readObject();
                    listaKand = (Kandydaci[]) message;
                    komunikaty.Kandydaci[] kandydaci = (komunikaty.Kandydaci[])message;
                    
                    for (int i = 0; i < kandydaci.length; i++) {
                        System.out.print(kandydaci[i].StringGlosowanie());
                    }
                
                }else if(Glosowanie){
                    
                    //tutaj jest sprawdzanie klienta, certyfikat
                    
                    
                    
                    

                    //wysyła prośbę o odesłanie listy kandydatów
                    sendMessage("Lista kandydatów");

                    //odczytuje liste kandydatow
                    message = in.readObject();
                    BigInteger[] lista = CardNS.getKandString(message);

                    //generuje n kart
                    Card[] karty = CardNS.genCards(lista, 10);
                    for (int i = 0; i < karty.length; i++) {
                        System.out.println(karty[i].toString());
                    }

                    //wysyła prośbę o odesłanie kluczy i modułu
                    sendMessage("Key");

                    //pobieranie kluczy
                    message = in.readObject();
                    BigInteger[] KLUCZE = (BigInteger[])message;

    //                for (int i = 0; i < KLUCZE.length; i++) {
    //                    System.out.println(KLUCZE[i]);
    //                }

                    //generowanie tablicy R do zaślepiania
                    BigInteger[] RRR = CVoting.RTablica(10, KLUCZE[1]);
                    //zaciemnia karty
                    CVoting.zaslonKarty(karty, RRR, KLUCZE[0], KLUCZE[1]);
    //                for (int i = 0; i < karty.length; i++) {
    //                    System.out.println(karty[i].toString());
    //                }

                    //wysyła n zaciemnionych kart do serwera
                    sendMessage(karty);
                    int jj = (Integer) in.readObject();
                    System.out.println("<wysłał server>" + jj);

                    //wysyła n-1 r, różne od tego który serwer wybrał
                    BigInteger TajnyR = RRR[jj];
    //                BigInteger[] OdR = RRR;
                    RRR[jj] = BigInteger.ZERO;
                    sendMessage(RRR);

                    //odbiera podpisaną (zaciemniona) karte
                    message = in.readObject();
                    Card karta = (Card) message;


                    //klient zdejmuje zaśleienie
                    CVoting.odslepPodKarte(karta, TajnyR, KLUCZE[1]);

                    //klient podpisuje wybrany głos i wysyła do serwera
                    CVoting.podpisanieGlosu(karta.votes[kartaGlos], KLUCZE[2], KLUCZE[3]);
                    sendMessage(karta.votes[kartaGlos]);

                    //pobiera stringa
                    message = in.readObject();
                    System.out.println((String)message);
                }else if(publikacja){
                    sendMessage("Proszę o wyniki");
                    
                    //odczytuje dane jeszcze nie wiem jaki typ
//                    message = in.readObject();
//                    int[] TabDan = (int[]) message;
//                    for (int i = 0; i < TabDan.length; i++) {
//                        System.out.println(TabDan[i]);
//                    }
                    
                    message = in.readObject();
                    Statistics stat = (Statistics)message;
//)                    int[] TabDan = (int[]) message;
//                    for (int i = 0; i < TabDan.length; i++) {
//                        System.out.println(TabDan[i]);
//                    }
                    sendMessage("kandydaci");
                    message = in.readObject();
                    Kandydaci[] kandydaci = (Kandydaci[])message;
                    
                    
                    System.out.println(stat.liczbaGlosujacych());
                    Iterator<HashMap<BigInteger, Integer>> iterator = stat.getWynikiDlaKandydatow().iterator();
                    
                    //String format = "%1$-28s %2$-4s %3$-5.2f %4$-1s\n";
                    String format = "%-30s\t %s \t %.2f %s \n";
                    wyniki="";
                    //wyniki+= String.format(format, "Imie i Nazwisko", "Wynik\n");
                    while (iterator.hasNext())
                    {
                        Entry<BigInteger,Integer> en=iterator.next().entrySet().iterator().next();
                        String s="";
                        for (Kandydaci e: kandydaci)
                            if (e.getPesel().equals(en.getKey()))
                                s=e.getImie()+"  "+e.getNazwisko();
                        System.out.println( s+":"+en.getValue()+":"+stat.wynikProcentowyDlaKandydata(en));
                        
                        //String.format(format, NR+".", imie, nazwisko);
                        wyniki+= String.format(format, s, en.getValue(), stat.wynikProcentowyDlaKandydata(en),"%");//s+":"+en.getValue()+":"+stat.wynikProcentowyDlaKandydata(en)+"\n";
                    }
                    
                    ArrayList<Card.Vote> w=stat.getVotes();
                    for (Card.Vote vote : w) {
                        System.out.println(vote.serial);
                    }
                    
                    
                    
                }
                

                
                
                
            }catch(ClassNotFoundException c){
                System.err.println("data received in unknown format");
            }
            
            
        }catch(UnknownHostException un){
            System.err.println("You are trying to connect to an unknown host!");
	}catch(IOException ex){}
	finally{
            try{
		in.close();
		out.close();
		socket.close();
            }catch(IOException ex){}
	}
    }
    
    private String wyniki;
    public String getWyniki() {
    	
    	return wyniki;
    }
    
    private void sendMessage(Object obj) {
        try{
            out.writeObject(obj);
            out.flush();
            //System.out.println("<wysłał klient>  " + obj.toString());
        }catch(IOException ex){
            System.out.println("błąd wysyłania!!!");
            ex.printStackTrace();
        }
    }
    
    public String getMessageServer() {
    	if(messageFromServer==null)
    		messageFromServer = "zle dziala program";
    	
    	return messageFromServer;
    }
    
}
    class MyHandshakeListener implements HandshakeCompletedListener {
        @Override
        public void handshakeCompleted(HandshakeCompletedEvent e) {
            System.out.println("Handshake succesful!");
            System.out.println("Using cipher suite: " + e.getCipherSuite());
        }
}

